<?php
/**
 * 数据库查询语句构建类
 *
 * @author edikud
 * @package Db
 * @copyright Copyright (c) 2019 Cola (http://www.mcooo.com)
 * @license GNU General Public License 2.0
 */

class Db_Query
{
    /**
     * 查询语句预结构,由数组构成,方便组合为SQL查询字符串
     *
     * @var array
     */
    private $_query = array();
	
    /**
     * 数据库Db类
     *
     * @var Db
     */
    private $_db;
	
    /**
     * 构造函数,引用数据库适配器作为内部数据
     *
     * @param Db $db 数据库适配器
     * @param string $prefix 前缀
     */
    public function __construct(Db $db)
    {
        $this->_db = $db;
        $this->_query = array();
    }

    /**
     * 设置查询字串属性值  
     * 
     * @param mixed $name 
     * @param mixed $value 
     * @access protected
     * @return void
     */
    protected function setQuery($name, $value)
    {
        $this->_query[$name] = $value;
    }

    /**
     * 叠加查询字串属性值
     * 
     * @param mixed $name 
     * @param mixed $value 
     * @access protected
     * @return void
     */
    protected function pushQuery($name, $value)
    {
        if (!isset($this->_query[$name])) {
            $this->_query[$name] = array();
        }

        $this->_query[$name][] = $value;
    }

    /**
     * 获取查询字串属性值
     *
     * @access public
     * @param mixed $name 属性名称
     * @return mixed
     */
    public function getQuery($name)
    {
        return isset($this->_query[$name]) ? $this->_query[$name] : NULL;
    }

    /**
     * 清除查询字串属性值
     *
     * @access public
     * @param string $attributeName 属性名称
     * @return Db_Query
     */
    public function removeQuery($name)
    {
        if (isset($this->_query[$name])) {
            $this->_query[$name] = NULL;
        }
        return $this;
    }
	
    /**
     * AND条件查询语句  
     *
     * @param string $condition 查询条件
     * @param mixed $param 条件值
     * @return Db_Query
     */
    public function where()
    {
        $args = func_get_args();
        if (!empty($args)) {
            $this->pushQuery('where', array('AND', $args));
        }

        return $this;
    }
	
    /**
     * OR条件查询语句  
     *
     * @param string $condition 查询条件
     * @param mixed $param 条件值
     * @return Db_Query
     */
    public function orWhere()
    {
        $args = func_get_args();
        if (!empty($args)) {
            $this->pushQuery('where', array('OR', $args));
        }

        return $this;
    }
	
    /**
     * 排序顺序(ORDER BY)  
     * 
     * @param mixed $column 排序的索引
     * @param mixed $sort 排序的方式(ASC, DESC, asc, desc)
     * @access public
     * @return Db_Query
     */
    public function order($column, $sort)
    {
        $this->pushQuery('order', 
		    array($column, !empty($sort) && false !== stripos('|ASC|DESC|', '|' . $sort . '|') ? ' ' . $sort : NULL));
        return $this;
    }

    /**
     * 连接表 
     * 
     * @param string $table 需要连接的表
     * @param string $condition 连接条件
     * @param string $op 连接方法(LEFT, RIGHT, INNER)
     * @return Db_Query
     */
    public function join($table, $condition, $op = 'INNER')
    {
        $this->pushQuery('join', 
		    array($table, $condition, $op && false !== stripos('|LEFT|RIGHT|INNER|', '|' . $op . '|') ? $op : 'INNER'));
        return $this;
    }

    /**
     * HAVING (HAVING)
     *
     * @param string $condition 查询
     * @param mixed $param 条件值
     * @return Db_Query
     */
    public function having()
    {
        $args = func_get_args();
        if (!empty($args)) {
            $this->pushQuery('having', array('AND', $args));
        }

        return $this;
    }	
	
    /**
     * 查询行数限制
     *
     * @param integer $limit 需要查询的行数
     * @return Db_Query
     */
    public function limit($limit)
    {
        $this->setQuery('limit', abs(intval($limit)));
        return $this;
    }

    /**
     * 查询行数偏移量
     *
     * @param integer $offset 需要偏移的行数
     * @return Db_Query
     */
    public function offset($offset)
    {
        $this->setQuery('offset', abs(intval($offset)));
        return $this;
    }

    /**
     * 分页查询
     *
     * @param integer $page 页数
     * @param integer $pageSize 每页行数
     * @return Db_Query
     */
    public function page($page, $pageSize)
    {
        return $this->limit($pageSize)
		    ->offset((max(intval($page), 1) - 1) * $pageSize);
    }
	
	
    /**
     * 集合聚集(GROUP BY)
     *
     * @param string $column 聚集的键值
     * @return Db_Query
     */
    public function group($column)
    {
        $this->setQuery('group', $this->applyPrefix($column));
        return $this;
    }	
	
    /**
     * 指定需要写入的栏目及其值  
     * 
     * @param array $values 
     * @access public
     * @return Db_Query
     */
    public function values(array $values)
    {
        $this->setQuery('values', $values);
        return $this;
    }	

    /**
     * 指定需要写入栏目及其值
     * 单行且不会转义引号
     *
     * @param string $key 栏目名称
     * @param mixed $value 指定的值
     * @param bool $escape 是否转义
     * @return Db_Query
     */
    public function expression($key, $value, $escape = true)
    {
        $this->pushQuery('expression', array($key, $value, $escape));
        return $this;
    }
	
    /**
     * 选择查询字段
     *
     * @access public
     * @param mixed $field 查询字段
     * @return Db_Query
     */
    public function select($field = '*')
    {
		$this->setQuery('action', 'SELECT');
        $args = func_get_args();

		$this->setQuery('columns', $args);
        return $this;
    }

    /**
     * 查询记录操作(SELECT)
     *
     * @param string $table 查询的表
     * @return Db_Query
     */
    public function from($table)
    {
		$this->setQuery('table', $table);
        return $this;
    }

    /**
     * 更新记录操作(UPDATE)
     *
     * @param string $table 需要更新记录的表
     * @return Db_Query
     */
    public function update($table)
    {
		$this->setQuery('action', 'UPDATE');
        $this->setQuery('table', $table);
        return $this;
    }

    /**
     * 删除记录操作(DELETE)
     *
     * @param string $table 需要删除记录的表
     * @return Db_Query
     */
    public function delete($table)
    {
		$this->setQuery('action', 'DELETE');
        $this->setQuery('table', $table);
        return $this;
    }

    /**
     * 插入记录操作(INSERT)
     *
     * @param string $table 需要插入记录的表
     * @return Db_Query
     */
    public function insert($table)
    {
		$this->setQuery('action', 'INSERT');
        $this->setQuery('table', $table);
        return $this;
    }
	
    /**
     * 直接Sql语句查询
     * 
     * @param string $sql           
     * @param mixed $parameter          
     * @return mixed
     */
    public function sql()
    {
		$this->setQuery('action', 'SQL');
        $args = func_get_args();
        if (!empty($args)) {
            $this->pushQuery('sql', $args);
        }

        return $this;
    }
	
    /**
     * getAdapter
     *
     * @return AdapterInterface
     */
    public function getAdapter()
    {
        return $this->_db->getAdapter();
    }
	
    public function selectDb($op)
    {
		return $this->_db->selectDb($op);
    }
	
    public function db()
    {
		return $this->_db;
    }
	
    /**
     * 返回插入数据的id
     * 
     * @return string boolean
     */
    public function lastInsertId($op = Db::WRITE)
    {
		return $this->getAdapter()
		    ->lastInsertId($this, $op);
    }
	
    /**
     * 返回实际受影响的行数
     * 
     * @return number boolean
     */
    public function affectedRows($op = Db::WRITE)
    {
		return $this->getAdapter()
		    ->affectedRows($this, $op);
    }

    /**
     * 一次取出所有行
     *
     * @param mixed $query 查询对象
     * @param array $filter 行过滤器函数,将查询的每一行作为第一个参数传入指定的过滤器中
     * @return array
     */
    public function fetchAll(array $filter = NULL, $op = Db::READ)
    {
		return $this->getAdapter()
		    ->fetchAll($this, $filter, $op);
    }

    /**
     * 一次取出一行
     *
     * @param mixed $query 查询对象
     * @param array $filter 行过滤器函数,将查询的每一行作为第一个参数传入指定的过滤器中
     * @return mixed
     */
    public function fetchRow(array $filter = NULL, $op = Db::READ)
    {
		return $this->getAdapter()
		    ->fetchRow($this, $filter, $op);
    }

    /**
     * 一次取出一个对象
     *
     * @param mixed $query 查询对象
     * @param array $filter 行过滤器函数,将查询的每一行作为第一个参数传入指定的过滤器中
     * @return mixed
     */
    public function fetchObject(array $filter = NULL, $op = Db::READ)
    {
		return $this->getAdapter()
		    ->fetchObject($this, $filter, $op);
    }
	
}
